import com.alibaba.druid.filter.FilterChain;
import com.alibaba.druid.filter.FilterEventAdapter;
import com.alibaba.druid.proxy.jdbc.*;
import com.mysql.cj.jdbc.ConnectionImpl;

import java.sql.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

// 收集 insert update delete sql 语句的过滤器
public class CollectIUDSQLFilter extends FilterEventAdapter {

    // 事务sql 临时存放 成功时剪切到 txSqlMapList 中，失败时移除
    private static Map<Long/*连接ID*/, List<String> /*事务中的SQL列表*/> momentTxSql = new ConcurrentHashMap<>();
    // 存放自动提交的SQL
    public static List<String> autoSql = new ArrayList<>();
    // 存放成功执行事务sql
    public static Map<String/*时间_连接ID*/,List<String> /*本次事务包含的SQL列表*/> txSqlMapList = new ConcurrentHashMap<>();

    // 手动提交事务
    @Override
    public void connection_commit(FilterChain chain, ConnectionProxy connection) throws SQLException {
        super.connection_commit(chain, connection);
        ConnectionImpl impl = (ConnectionImpl) connection.getRawObject();
        System.out.println("commit:" + impl.getCatalog() + " 连接id:" + impl.getId());

        String key = System.currentTimeMillis() + "_" + impl.getId();
        txSqlMapList.put(key,momentTxSql.remove(impl.getId()));
    }

    // 事务回滚
    @Override
    public void connection_rollback(FilterChain chain, ConnectionProxy connection) throws SQLException {
        super.connection_rollback(chain, connection);
        ConnectionImpl impl = (ConnectionImpl) connection.getRawObject();
        System.out.println("rollback:" + impl.getCatalog() + " 连接id:" + impl.getId());
        momentTxSql.remove(impl.getId());
    }

    // 回滚到指定保存点
//    @Override
//    public void connection_rollback(FilterChain chain, ConnectionProxy connection, Savepoint savepoint) throws SQLException {
//        super.connection_rollback(chain, connection, savepoint);
//        ConnectionImpl impl = (ConnectionImpl) connection.getRawObject();
//        System.out.println("rollback:" + impl.getCatalog() + " 连接id:" + impl.getId() + " savepoint:[id:" + savepoint.getSavepointId() + ",name:" + savepoint.getSavepointName()+"]");
//    }

    // 调用存储过程后 调用
    @Override
    protected void statementPrepareCallAfter(CallableStatementProxy statement) {
        super.statementPrepareCallAfter(statement);
        System.out.println("statementPrepareCallAfter: " + statement.getLastExecuteSql() + " | " + statement.getSql() + " | " + statement.getBatchSql());
    }

    // 结果集开启后 调用
    @Override
    protected void resultSetOpenAfter(ResultSetProxy resultSet) {
        super.resultSetOpenAfter(resultSet);
        System.out.println("resultSetOpenAfter:"+resultSet.getSql());
    }

    // 执行Update方法后 调用
    @Override
    protected void statementExecuteUpdateAfter(StatementProxy statement, String sql, int updateCount) {
        System.out.println("---");
        try {
            ConnectionImpl connection = (ConnectionImpl) statement.getConnection();
            boolean auto = connection.getAutoCommit();
            System.out.println(auto ? "自动提交" : "事务");
            System.out.println("连接ID："+connection.getId());
            System.out.println("当前事件名称："+statement.getLastExecuteType().name());
            sql = assembleSql(sql,statement.getParameters());
            System.out.println("statementExecuteUpdateAfter:"+sql +  " changeCount:" + updateCount);
            if (auto){
                autoSql.add(sql);
            }else {
                List<String> txItemSqlList = momentTxSql.computeIfAbsent(connection.getId(), k -> new ArrayList<>());
                txItemSqlList.add(sql);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 执行sql方法后调用
    @Override
    protected void statementExecuteAfter(StatementProxy statement, String sql, boolean result) {
        super.statementExecuteAfter(statement, sql, result);
        System.out.println("statementExecuteAfter:"+sql);
    }

    // 执行批量sql方法调用
    @Override
    protected void statementExecuteBatchAfter(StatementProxy statement, int[] result) {
        super.statementExecuteBatchAfter(statement, result);
        System.out.println("statementExecuteBatchAfter:" + statement.getBatchSql() + " " + Arrays.toString(result));
    }

    // 执行sql失败后调用
    @Override
    protected void statement_executeErrorAfter(StatementProxy statement, String sql, Throwable error) {
        super.statement_executeErrorAfter(statement, sql, error);
        System.out.println("error:"+sql);
        String finalSql = assembleSql(sql,statement.getParameters());
        autoSql.removeIf(s -> s.equals(finalSql));
    }

    private String assembleSql(String sql,Map<Integer, JdbcParameter> parameterMap){
        for (Integer key : parameterMap.keySet()){
            JdbcParameter parameter = parameterMap.get(key);
            sql = sql.replaceFirst("\\?", parameter.getSqlType() == 12 ? "'" + parameter.getValue() + "'" : String.valueOf(parameter.getValue()));
        }
        return sql;
    }
}
